/* eslint-disable max-classes-per-file */

import Phaser from 'phaser'
import _ from 'lodash'

import imgHero from './assets/tilemaps/tiles/hero.png'
import imgDoor from './assets/tilemaps/tiles/door.png'
import imgItem from './assets/tilemaps/tiles/item.png'
import imgHead from './assets/tilemaps/tiles/head.png'
import imgYellowKey from './assets/tilemaps/tiles/key_yellow.png'
import imgBlueKey from './assets/tilemaps/tiles/key_blue.png'
import imgRedKey from './assets/tilemaps/tiles/key_red.png'
import imgEnemy from './assets/tilemaps/tiles/enemy.png'
import imgFloorUp from './assets/tilemaps/tiles/floor_up.png'
import imgFloorDown from './assets/tilemaps/tiles/floor_down.png'
import imgWall from './assets/tilemaps/tiles/wall.png'
import imgRoad from './assets/tilemaps/tiles/road.png'
import imgLava from './assets/tilemaps/tiles/lava.png'
import map0 from './assets/tilemaps/maps/map0.json'
import map1 from './assets/tilemaps/maps/map1.json'
import sndBgm0 from './assets/audio/sndBGM0.wav'
import sndBgm1 from './assets/audio/sndBGM1.wav'
import sndStep from './assets/audio/sndStep.wav'
import sndStair from './assets/audio/sndStair.wav'
import sndItem1 from './assets/audio/sndItem1.wav'
import sndItem2 from './assets/audio/sndItem2.wav'
import CONST from './const'
import tools from './js/tools'
import enemyData from './assets/tilemaps/enemy-data.json'
import {
  HeroSprite, FloorUpSprite, FloorDownSprite,
  SmallBottleSprite, BigBottleSprite,
  RedDoorSprite, YellowDoorSprite, BlueDoorSprite,
  RedKeySprite, YellowKeySprite, BlueKeySprite,
  RedGemSprite, BlueGemSprite,
  IronShieldSprite,
  SkeletonManSprite, SkeletonSoldierSprite,
  RedSlimeSprite, GreenSlimeSprite, BlackSlimeSprite,
  SmallBatSprite,
  OrcFaceManSprite,
  PrimaryWizardSprite
} from './js/sprite'

// 场景类的基类，不用于创建场景。
class BaseScene extends Phaser.Scene {
  constructor () {
    super()

    this.mapIndex = -1
    this.objects = []
  }

  setMapIndex (index) {
    this.mapIndex = index
    Phaser.Scene.call(this, { key: `${index}` })
  }

  preload () {
    this.load.image('hero', imgHero)
    this.load.image('head', imgHead)
    this.load.image('yellowKey', imgYellowKey)
    this.load.image('blueKey', imgBlueKey)
    this.load.image('redKey', imgRedKey)
    this.load.image('floorUp', imgFloorUp)
    this.load.image('floorDown', imgFloorDown)
    this.load.image('door', imgDoor)
    this.load.image('enemy', imgEnemy)
    this.load.image('item', imgItem)
    this.load.image('lava', imgLava)
    this.load.image('wall', imgWall)
    this.load.image('road', imgRoad)

    this.load.spritesheet(CONST.SPRITE_SHEET_HERO,
      imgHero, { frameWidth: 32, frameHeight: 32 })
    this.load.spritesheet(CONST.SPRITE_SHEET_ENEMY,
      imgEnemy, { frameWidth: 32, frameHeight: 32 })
    this.load.spritesheet(CONST.SPRITE_SHEET_ITEM,
      imgItem, { frameWidth: 32, frameHeight: 32 })
    this.load.spritesheet(CONST.SPRITE_SHEET_DOOR,
      imgDoor, { frameWidth: 32, frameHeight: 32 })
    this.load.spritesheet(CONST.SPRITE_SHEET_FLOOR_UP,
      imgFloorUp, { frameWidth: 32, frameHeight: 32 })
    this.load.spritesheet(CONST.SPRITE_SHEET_FLOOR_DOWN,
      imgFloorDown, { frameWidth: 32, frameHeight: 32 })

    // 从json文件创建地图
    const jsonMaps = [map0, map1]
    this.load.tilemapTiledJSON(`map${this.mapIndex}`, jsonMaps[this.mapIndex])

    // bgms
    const sndBgms = [sndBgm0, sndBgm1]
    this.load.audio(`bgm${this.mapIndex}`, sndBgms[this.mapIndex])
    // sound effects
    this.load.audio('step', [sndStep])
    this.load.audio('stair', [sndStair])
    this.load.audio('item1', [sndItem1])
    this.load.audio('item2', [sndItem2])
  }

  create () {
    const map = this.makeMap()
    this.map = map // 记为成语方便外部访问

    // create hero
    this.hero = new HeroSprite(this)
    this.hero.x = tools.getMapEnterPos(map).x
    this.hero.y = tools.getMapEnterPos(map).y

    // timer to update background
    this.time.addEvent({
      delay: 200, callback: this.onUpdateBackground, callbackScope: this, loop: true
    })
  }

  makeMap () {
    const map = this.make.tilemap({ key: `map${this.mapIndex}` })
    map.addTilesetImage('lava', 'lava')
    map.addTilesetImage('road', 'road')
    map.addTilesetImage('wall', 'wall')

    map.addTilesetImage('floorUp', 'floorUp')
    map.addTilesetImage('floorDown', 'floorDown')
    map.addTilesetImage('door', 'door')
    map.addTilesetImage('enemy', 'enemy')
    map.addTilesetImage('item', 'item')

    this.currentBaseLayer = map.createLayer('base', ['lava', 'road', 'wall'])

    // create new sprites
    let objects = []
    let objs = map.createFromObjects('object',
      { name: 'FloorUp', key: 'floorUp', classType: FloorUpSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'FloorDown', key: 'floorDown', classType: FloorDownSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'YellowDoor', key: CONST.SPRITE_SHEET_DOOR, classType: YellowDoorSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'BlueDoor', key: CONST.SPRITE_SHEET_DOOR, classType: BlueDoorSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'RedDoor', key: CONST.SPRITE_SHEET_DOOR, classType: RedDoorSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'YellowKey', key: CONST.SPRITE_SHEET_ITEM, classType: YellowKeySprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'BlueKey', key: CONST.SPRITE_SHEET_ITEM, classType: BlueKeySprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'RedKey', key: CONST.SPRITE_SHEET_ITEM, classType: RedKeySprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'SmallBottle', key: CONST.SPRITE_SHEET_ITEM, classType: SmallBottleSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'BigBottle', key: CONST.SPRITE_SHEET_ITEM, classType: BigBottleSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'BlueGem', key: CONST.SPRITE_SHEET_ITEM, classType: RedGemSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'RedGem', key: CONST.SPRITE_SHEET_ITEM, classType: BlueGemSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'IronShield', key: CONST.SPRITE_SHEET_ITEM, classType: IronShieldSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'SkeletonMan', key: CONST.SPRITE_SHEET_ENEMY, classType: SkeletonManSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'RedSlime', key: CONST.SPRITE_SHEET_ENEMY, classType: RedSlimeSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'GreenSlime', key: CONST.SPRITE_SHEET_ENEMY, classType: GreenSlimeSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'BlackSlime', key: CONST.SPRITE_SHEET_ENEMY, classType: BlackSlimeSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'SmallBat', key: CONST.SPRITE_SHEET_ENEMY, classType: SmallBatSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'SkeletonSoldier', key: CONST.SPRITE_SHEET_ENEMY, classType: SkeletonSoldierSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'PrimaryWizard', key: CONST.SPRITE_SHEET_ENEMY, classType: PrimaryWizardSprite })
    objects = objects.concat(objs)

    objs = map.createFromObjects('object',
      { name: 'OrcFaceMan', key: CONST.SPRITE_SHEET_ENEMY, classType: OrcFaceManSprite })
    objects = objects.concat(objs)

    this.objects = objects

    // 移除本层已经销毁的对象
    const objectsDestroyed = this.game.registry.get('destroyedObjectDB')[this.mapIndex]
    const objectsToDestroy = []
    if (objectsDestroyed) {
      objectsDestroyed.forEach((itemDestroyed) => {
        for (let idx = 0; idx < this.objects.length; idx += 1) {
          const obj = this.objects[idx]
          if (Math.abs(itemDestroyed.x - obj.x) < 4 && Math.abs(itemDestroyed.y - obj.y) < 4) {
            objectsToDestroy.push(obj)
          }
        }
      })
    }
    objectsToDestroy.forEach((obj) => {
      obj.destroy()
    })

    // 初始化怪物属性
    BaseScene.initEnemyProperty(objects)

    // 绘制怪物能对英雄造成损失的血量
    this.drawHpLoose()

    // draw game bound rect
    this.drawGameBoundRect()
    // draw status panel
    this.drawStatusPanel()

    // bgm
    const bgm = this.sound.add(`bgm${this.mapIndex > 0 ? 1 : 0}`)
    bgm.play({ loop: true })

    return map
  }

  calcHpLoose (enemy) {
    const playerAttack = this.game.registry.get('playerAttack')
    const playerDefense = this.game.registry.get('playerDefense')
    const playerHp = this.game.registry.get('playerHp')

    if (playerAttack <= enemy.defense) {
      return NaN // 攻击力不够，打不过
    }
    const round = enemy.hp / (playerAttack - enemy.defense)

    let hpLoose = (enemy.attack - playerDefense) * round
    if (hpLoose !== Math.floor(hpLoose)) {
      hpLoose = Math.floor(hpLoose)
    }

    if (hpLoose >= playerHp) {
      return NaN // 损失血量太多，英雄会挂掉
    }

    return hpLoose
  }

  drawHpLoose () {
    if (this.hpGroup) {
      this.hpGroup.destroy(true)
      this.hpGroup = null
    }
    this.hpGroup = this.add.group()
    this.objects.forEach((enemy) => {
      if (enemy.attack && enemy.defense) {
        const hpLoose = this.calcHpLoose(enemy)
        const text = this.add.text(enemy.x - CONST.BLOCK_SIZE / 4, enemy.y + CONST.BLOCK_SIZE / 4, `${hpLoose}`)
        this.hpGroup.add(text)
        text.setFontSize(10)
        text.setColor('#FFFFFF')
        text.setBackgroundColor('#000000')
        text.setFontFamily('Microsoft YaHei')

        if (Number.isNaN(hpLoose)) {
          text.setColor('#FF0000')
          // text.setBackgroundColor('#EFEFDA');
        } else if (hpLoose === 0) {
          text.setColor('#99CC33')
        }
      }
    })
  }

  // 从配置文件中读取怪物属性，并赋值到怪物上
  static initEnemyProperty (objects) {
    objects.forEach((obj) => {
      const dataFind = _.find(enemyData.enemies, { name: obj.name })
      if (dataFind) {
        _.assign(obj, dataFind)
      }
    })
  }

  /**
   * 跳转到指定的地图. 英雄会重新创建以正确显示，调用前需要先调整英雄位置.
   * @param {int} index 地图的索引
   * @param {boolean} bGoUP true - 往高层移动, false - 往底层移动
   * @returns {Phaser.Tilemaps.Tilemap}
   */
  goToScene (index) {
    if (index < CONST.MIN_MAP_INDEX || index > CONST.MAX_MAP_INDEX) {
      return // 超出范围了
    }

    this.scene.start(`${index}`)

    this.handleSceneChange()
  }

  goNextScene () {
    if (this.mapIndex === CONST.MAX_MAP_INDEX) {
      return // 没有后一个场景了
    }

    this.scene.start(`${this.mapIndex + 1}`)

    this.handleSceneChange()
  }

  goPrevScene () {
    if (this.mapIndex === CONST.MIN_MAP_INDEX) {
      return // 没有前一个场景了
    }

    this.scene.start(`${this.mapIndex - 1}`)

    this.handleSceneChange()
  }

  handleSceneChange () {
    // 停止音乐播放
    this.sound.removeAll()

    // 音效
    const stair = this.sound.add('stair')
    stair.play()
  }

  update () {
    this.playerLevelText.setText(this.game.registry.get('playerLevel'))
    this.playerHpText.setText(this.game.registry.get('playerHp'))
    this.playerAttackText.setText(this.game.registry.get('playerAttack'))
    this.playerDefenseText.setText(this.game.registry.get('playerDefense'))
    this.playerGoldText.setText(this.game.registry.get('playerGold'))
    this.playerExpText.setText(this.game.registry.get('playerExp'))
    this.playerYellowKeyText.setText(this.game.registry.get('playerYellowKeyCount'))
    this.playerBlueKeyText.setText(this.game.registry.get('playerBlueKeyCount'))
    this.playerRedKeyText.setText(this.game.registry.get('playerRedKeyCount'))
  }

  drawGameBoundRect () {
    const graphics = this.add.graphics()
    graphics.lineStyle(4, 0xcc6600, 1)
    // left bound
    graphics.strokeRect(32 - 2, 32 - 2, 32 * 4 + 4, 32 * 11 + 4)
    // right bound
    graphics.strokeRect(32 * 6 - 2, 32 - 2, 32 * 11 + 4, 32 * 11 + 4)
  }

  drawStatusPanel () {
    const graphics = this.add.graphics()
    graphics.lineStyle(4, 0xcc6600, 1)

    this.add.image(32 * 2, 32 + 16 + 4, 'head')

    const level = this.game.registry.get('playerLevel')
    this.playerLevelText = this.add.text(32 * 3 - 2, 32 + 10, level).setFont('22px Impact').setColor('#ffffff')
    this.add.text(32 * 3 + 32, 32 + 13, '级').setFont('16px Impact').setColor('#ffffff')

    const hp = this.game.registry.get('playerHp')
    this.add.text(32 + 10, 64 + 8, '生命').setFont('16px Impact').setColor('#ffffff')
    this.playerHpText = this.add.text(32 * 3 + 10, 64 + 8, hp).setFont('16px Impact').setColor('#ffffff')

    const attack = this.game.registry.get('playeAttack')
    this.add.text(32 + 10, 64 + 24 + 8, '攻击').setFont('16px Impact').setColor('#ffffff')
    this.playerAttackText = this.add.text(32 * 3 + 10, 64 + 24 + 8, attack).setFont('16px Impact').setColor('#ffffff')

    const defense = this.game.registry.get('playeDefense')
    this.add.text(32 + 10, 64 + 24 * 2 + 8, '防御').setFont('16px Impact').setColor('#ffffff')
    this.playerDefenseText = this.add.text(32 * 3 + 10, 64 + 24 * 2 + 8, defense).setFont('16px Impact').setColor('#ffffff')

    const gold = this.game.registry.get('playerGold')
    this.add.text(32 + 10, 64 + 24 * 3 + 8, '金币').setFont('16px Impact').setColor('#ffffff')
    this.playerGoldText = this.add.text(32 * 3 + 10, 64 + 24 * 3 + 8, gold).setFont('16px Impact').setColor('#ffffff')

    const exp = this.game.registry.get('playerExp')
    this.add.text(32 + 10, 64 + 24 * 4 + 8, '经验').setFont('16px Impact').setColor('#ffffff')
    this.playerExpText = this.add.text(32 * 3 + 10, 64 + 24 * 4 + 8, exp).setFont('16px Impact').setColor('#ffffff')

    graphics.beginPath()
    graphics.moveTo(32, 64 + 24 * 5 + 10)
    graphics.lineTo(32 * 5, 64 + 24 * 5 + 10)
    graphics.strokePath()

    // yellow key count
    this.add.image(32 + 16 + 6, 32 * 7 - 10, 'yellowKey')
    const yellowKeyCount = this.game.registry.get('playerYellowKeyCount')
    this.playerYellowKeyText = this.add.text(96 + 10, 32 * 7 - 20, yellowKeyCount).setFont('16px Impact').setColor('#ffffff')

    // blue key count
    this.add.image(32 + 16 + 6, 32 * 8 - 10, 'blueKey')
    const blueKeyCount = this.game.registry.get('playerBlueKeyCount')
    this.playerBlueKeyText = this.add.text(96 + 10, 32 * 8 - 20, blueKeyCount).setFont('16px Impact').setColor('#ffffff')

    // red key count
    this.add.image(32 + 16 + 6, 32 * 9 - 10, 'redKey')
    const redKeyCount = this.game.registry.get('playerRedKeyCount')
    this.playerRedKeyText = this.add.text(96 + 10, 32 * 9 - 20, redKeyCount).setFont('16px Impact').setColor('#ffffff')

    graphics.beginPath()
    graphics.moveTo(32, 300)
    graphics.lineTo(32 * 5, 300)
    graphics.strokePath()

    // current map text holder
    const playerCurrentMapText = this.add.text(32 * 2 + 6, 305, '').setFont('16px Impact').setColor('#ffffff').setText('')
    if (this.mapIndex === 0) {
      playerCurrentMapText.setText('序     章')
    } else {
      playerCurrentMapText.setText(`第 ${this.mapIndex} 关`)
    }

    graphics.moveTo(32, 330)
    graphics.lineTo(32 * 5, 330)
    graphics.strokePath()
  }

  onUpdateBackground () {
    // flip the given tile to simulate animation
    if (this.currentBaseLayer) {
      this.currentBaseLayer.forEachTile((tile) => {
        if (tile.index === 88 || tile.index === 84) {
          tile.toggleFlipX()
        }
      })
    }
  }
}

class Scene0 extends BaseScene {
  constructor () {
    super()

    this.setMapIndex(0)
  }
}

class Scene1 extends BaseScene {
  constructor () {
    super()

    this.setMapIndex(1)
  }
}

const config = {
  type: Phaser.AUTO,
  parent: 'phaser-example',
  width: 576,
  height: 416,
  scene: [Scene0, Scene1],
  title: '魔塔小游戏',
  version: '1.0.0'
}

// eslint-disable-next-line no-unused-vars
const game = new Phaser.Game(config)

// 记录每一层已经销毁的对象
// 格式
// {
//  "1": [{x1, y1}],
//  "2": [{x3, y3}, {x4, y4}],
//  ...
// }
game.registry.set('destroyedObjectDB', {})

game.registry.set('playerLevel', 1)
game.registry.set('playerHp', 1000)
game.registry.set('playerAttack', 10)
game.registry.set('playerDefense', 20)
game.registry.set('playerGold', 0)
game.registry.set('playerExp', 0)
game.registry.set('playerYellowKeyCount', 1000)
game.registry.set('playerBlueKeyCount', 1000)
game.registry.set('playerRedKeyCount', 1000)
